// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Version2ProtocolTests.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Test.Robotics.Hardware.DAC
{
    using System;
    using System.IO;
    using System.Text;
    using Microsoft.Robotics.Hardware.DAC;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Tests for the Version 2 HW Protocol
    /// </summary>
    [TestClass]
    public class Version2ProtocolTests
    {
        /// <summary>
        /// A test string
        /// </summary>
        private const string TestString = "Hello World";

        /// <summary>
        /// Test bytes
        /// </summary>
        private static byte[] testData = System.Text.Encoding.UTF8.GetBytes(TestString);

        /// <summary>
        /// Track if a message got parsed
        /// </summary>
        private bool messageParsed;

        /// <summary>
        /// GetNextSequenceNumber test
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void GetNextSequenceNumberTest()
        {
            byte[] buffer = new byte[256];
            MockTransport transport = new MockTransport(buffer);
            using (Version2Protocol protocol = new Version2Protocol(transport))
            {
                for (ushort i = 1; i < ushort.MaxValue; i++)
                {
                    Assert.AreEqual(protocol.GetNextSequenceNumber(), i);
                }

                Assert.AreEqual(protocol.GetNextSequenceNumber(), 65535);
                Assert.AreEqual(protocol.GetNextSequenceNumber(), 1);
            }
        }

        /// <summary>
        /// Test message extraction
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void ExtractMessageTest()
        {
            byte[] buffer = new byte[256];

            MockTransport transport = new MockTransport(buffer);
            Version2Protocol protocol = new Version2Protocol(transport);

            int size;

            this.GenerateTestPacket(42, 43, testData, 1, buffer, out size);
            MockEventArgs eventArgs = new MockEventArgs(transport, buffer);
            eventArgs.BytesTransferred = size;

            this.messageParsed = false;
            Assert.IsTrue(protocol.ExtractMessageData(eventArgs, this.ExtractCommandMessage));
            Assert.IsTrue(this.messageParsed);
        }

        /// <summary>
        /// Test RunCommand
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void RunCommandTest()
        {
            byte[] buffer = new byte[256];

            MockTransport transport = new MockTransport(buffer);
            Version2Protocol protocol = new Version2Protocol(transport);
            MockEventArgs eventArgs = new MockEventArgs(transport, buffer);
            TestCommandMessage command = new TestCommandMessage();

            protocol.RunCommand(command, eventArgs);

            // verify the eventArgs data were populated correctly using ExtractMessageData
            eventArgs.BytesTransferred = 23; // learned this by stepping through code.
            this.messageParsed = false;
            Assert.IsTrue(protocol.ExtractMessageData(eventArgs, this.ExtractCommandMessage));
            Assert.IsTrue(this.messageParsed);
        } 

        /// <summary>
        /// Extracts and processes a CommandMessage from a DriveBoard data buffer
        /// This is the delegate passed to Version2Protocol.ExtractMessageData
        /// </summary>
        /// <param name="buffer">The buffer</param>
        /// <param name="commandId">The commandid</param>
        /// <param name="seqno">The seqno</param>
        /// <param name="offset">The offset</param>
        /// <param name="length">The length</param>
        private void ExtractCommandMessage(byte[] buffer, byte commandId, int seqno, int offset, int length)
        {
            this.messageParsed = true;
            Assert.AreEqual(commandId, 42);
            Assert.AreEqual(seqno, 1);
            Assert.AreEqual(TestString, Encoding.UTF8.GetString(buffer, offset, length));
        }

        /// <summary>
        /// Create a test packet
        /// </summary>
        /// <param name="commandType">The command type</param>
        /// <param name="responseCode">The response code</param>
        /// <param name="data">The command data</param>
        /// <param name="sequenceNumber">The sequence number</param>
        /// <param name="buffer">The buffer to write to</param>
        /// <param name="size">The final size of the buffer</param>
        private void GenerateTestPacket(byte commandType, byte responseCode, byte[] data, ushort sequenceNumber, byte[] buffer, out int size)
        {
            ushort packetHeader = 0xBEEF;
            ushort packetFooter = 0xDEAD;

            MemoryStream ms = new MemoryStream(buffer, 0, buffer.Length, true);
            using (BinaryWriter bw = new BinaryWriter(ms))
            {
                ms = null;

                // Write the header
                bw.Write(packetHeader);
                bw.Write(sequenceNumber);

                bw.Write(commandType);
                bw.Write(responseCode);

                // Write the body
                bw.Write((ushort)data.Length);
                bw.Write(data);

                // Write the crc
                bw.Write(Crc16.ComputeHash(buffer, 0, (int)bw.BaseStream.Position));

                // Write the footer
                bw.Write(packetFooter);

                size = (int)bw.BaseStream.Position;
            }
        }
    }
}
